package org.gjy.m8.thread;

import com.google.common.base.Stopwatch;
import com.google.common.collect.Lists;
import org.junit.Test;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * ❑ 创建异步计算，并获取计算结果
 * ❑ 使用非阻塞操作提升吞吐量
 * ❑ 设计和实现异步API
 * ❑ 如何以异步的方式使用同步的API
 * ❑ 如何对两个或多个异步操作进行流水线和合并操作
 * ❑ 如何处理异步操作的完成状态
 *
 * @author 宫静雨
 * @version 1.0
 * @since 2023-08-01 08:37:02
 */
public class CfTest {

    private static final AtomicInteger max = new AtomicInteger(0);
    private static final AtomicInteger sum = new AtomicInteger(0);

    private void sleep() {
        try {
            int s = ThreadLocalRandom.current().nextInt(10) + 1;
            sum.addAndGet(s);
            if (max.get() <= s) {
                max.set(s);
            }
            TimeUnit.MILLISECONDS.sleep(s);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    @Test
    public void test8() {
        ArrayList<String> list = Lists.newArrayList();
        for (int i = 0; i < 5000; i++) {
            list.add(UUID.randomUUID().toString());
        }
        Stopwatch stopwatch = Stopwatch.createStarted();

        List<String> l = list.stream().parallel().map(s -> {
            sleep();
            return s.split("-")[0];
        }).collect(Collectors.toList());

        stopwatch.stop();
        System.out.println(stopwatch.elapsed(TimeUnit.MILLISECONDS));

        System.out.println(l);
        System.out.println(l.size());
        System.out.println("max.get() = " + max.get());
        System.out.println("sum.get() = " + sum.get());
    }

    @Test
    public void test7() {
        ArrayList<String> list = Lists.newArrayList();
        for (int i = 0; i < 5000; i++) {
            list.add(UUID.randomUUID().toString());
        }
        Stopwatch stopwatch = Stopwatch.createStarted();

        int processors = Runtime.getRuntime().availableProcessors();
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                processors,
                processors * 2,
                20,
                TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(processors * 2),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy()
        );

        List<CompletableFuture<String>> collect = list.stream().map(s -> CompletableFuture.supplyAsync(() -> {
            sleep();
            return s.split("-")[0];
        }, executor)).collect(Collectors.toList());

        List<String> l = collect.stream().map(CompletableFuture::join).collect(Collectors.toList());
        stopwatch.stop();
        System.out.println(stopwatch.elapsed(TimeUnit.MILLISECONDS));

        System.out.println(l);
        System.out.println(l.size());
        System.out.println("max.get() = " + max.get());
        System.out.println("sum.get() = " + sum.get());
    }

    @Test
    public void test6() {
        int processors = Runtime.getRuntime().availableProcessors();
        System.out.println("processors = " + processors);
    }

    @Test
    public void test5() {
        ArrayList<Integer> integers = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8, 9);
        List<CompletableFuture<BigDecimal>> collect = integers.stream()
                .map(integer -> CompletableFuture.supplyAsync(() -> {
                    int i = ThreadLocalRandom.current().nextInt(5);
                    System.out.println("i = " + i);
                    try {
                        TimeUnit.SECONDS.sleep(i);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    return BigDecimal.valueOf(integer);
                })).collect(Collectors.toList());
        List<BigDecimal> list = collect.stream().map(CompletableFuture::join).collect(Collectors.toList());
        System.out.println(list);
    }

    private Future<BigDecimal> getPriceAsync(Integer code) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                int i = ThreadLocalRandom.current().nextInt(5);
                System.out.println("i = " + i);
                TimeUnit.SECONDS.sleep(i);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            return BigDecimal.valueOf(code);
        });
    }

    @Test
    public void test4() throws Exception {
        Future<BigDecimal> priceAsync = getPriceAsync(1);
        Future<BigDecimal> priceAsync2 = getPriceAsync(2);
        Future<BigDecimal> priceAsync3 = getPriceAsync(3);
        System.out.println("priceAsync.get() = " + priceAsync.get());
        System.out.println("priceAsync2.get() = " + priceAsync2.get());
        System.out.println("priceAsync3.get() = " + priceAsync3.get());
    }

    @Test
    public void test3() throws Exception {
        ExecutorService pool = Executors.newFixedThreadPool(1);
        for (int i = 0; i < 5; i++) {
            final int a = i;
            Future<Future<BigDecimal>> submit = pool.submit(() -> getPriceAsync(a));
            BigDecimal bigDecimal = submit.get().get();
            System.out.println("bigDecimal = " + bigDecimal);
        }
    }

    @Test
    public void test2() {
        ExecutorService pool = Executors.newFixedThreadPool(1);
        Future<Integer> submit = pool.submit(() -> {
            System.out.println(Thread.currentThread().getName());
            int i = ThreadLocalRandom.current().nextInt(5);
            TimeUnit.SECONDS.sleep(7);
            System.out.println("i = " + i);
            return i;
        });
        try {
//            TimeUnit.SECONDS.sleep(5);
            Integer integer = submit.get(3, TimeUnit.SECONDS);
            System.out.println("integer = " + integer);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Test
    public void test1() {
        CompletableFuture<Void> async = CompletableFuture.runAsync(() -> {
            System.out.println(Thread.currentThread().getName());
        });
    }
}
